package com.acme.bankapp.domain.bank;


import com.acme.bankapp.domain.bank.account.Account;
import com.acme.bankapp.domain.bank.client.Client;
import com.acme.bankapp.domain.bank.client.ClientInfo;
import com.acme.bankapp.domain.bank.exception.AccountExistsException;
import com.acme.bankapp.domain.bank.exception.ClientExistsException;
import com.acme.bankapp.service.bank.listener.ClientRegistrationListener;

import java.io.Serializable;
import java.util.*;

/**
 * This class represents bank
 *
 * @author Alexander Kotov
 */

public class Bank implements Serializable {
    private Map<ClientInfo, Client> idToClient = new HashMap<>();
    private List<ClientRegistrationListener> listeners = new ArrayList<>();

    public Bank() {
        addClientRegistrationListener(new ClientRegistrationListener() {
            /**
             * {@inheritDoc}
             */
            @Override
            public void onClientAdded(Client client) {
                System.out.format("Time: %s, client: %s%n", new Date(), client);
            }
        });
    }

    public Bank(ClientRegistrationListener... listeners) {
        this();
        for (ClientRegistrationListener listener : listeners) {
            addClientRegistrationListener(listener);
        }
    }

    /**
     * This method add client registration listener
     *
     * @param listener listener to add
     */
    public void addClientRegistrationListener(ClientRegistrationListener listener) {
        if (listener == null) {
            throw new IllegalArgumentException("Invalid listener object");
        }
        this.listeners.add(listener);
    }

    /**
     * @return bank idToClient
     */
    public Collection<Client> getClients() {
        return Collections.unmodifiableCollection(idToClient.values());
    }

    public Client getClientById(ClientInfo id) {
        return idToClient.get(id);
    }

    /**
     * This method for add client object to bank object
     *
     * @param client represents {@link Client} object
     */
    public void addClient(Client client)
            throws ClientExistsException {
        if (client == null) {
            throw new IllegalArgumentException();
        }
        final ClientInfo clientInfo = client.getClientInfo();
        if (idToClient.containsKey(clientInfo)) {
            throw new ClientExistsException(clientInfo);
        }
        idToClient.put(clientInfo, client);
        for (ClientRegistrationListener listener : listeners) {
            listener.onClientAdded(client);
        }
    }

    public void addAccountForClient(ClientInfo clientInfo, Account account) throws AccountExistsException {
        final Client existingClient = idToClient.get(clientInfo);
        if (existingClient == null) {
            final Client newClient = new Client(clientInfo.getName(), clientInfo.getGender(), clientInfo.getEmailAddress());
            try {
                addClient(newClient);
            }
            catch (ClientExistsException e) {
            }
            newClient.addAccount(account);
        } else {
            existingClient.addAccount(account);
        }
    }
}
